package hxm.core;

import haxe.macro.Context;
import haxe.macro.Expr;

import tink.macro.tools.MacroTools;

import hxm.core.MExpr;

#if macro
using tink.macro.tools.ExprTools;
using hxm.core.MacroHelper;
#end

/**
 * The parser parses an Expr to a corresponging MExpr.
 * 
 * @author Simon Krajewski
 */
class Parser 
{
	#if macro
	
	public static function parseExpr(exprs:Array<Expr>, outerScope = true):MExpr
	{
		var mexprs:Array<MExpr> = [];

		if (exprs.length == 0)
			return MBlock([]).at(Context.currentPos());

		while (exprs.length > 0)
		{		
			var expr = exprs.shift();
			switch(expr.expr)
			{
				default:
					mexprs.push(MExpr(expr).at(expr.pos));
				case EConst(c):
					switch(c)
					{
						case CType(t):
							mexprs.push(MType(MExpr(expr).at(expr.pos)).at(expr.pos));
						default:
							mexprs.push(MExpr(expr).at(expr.pos));
					}
				case EBlock(exprs):
					var sub = [];
					for (e in exprs)
						sub.push(parseExpr(exprs));
					mexprs.push(MSub(MBlock(sub).at(expr.pos)).at(expr.pos));
				case EReturn(v):
					var ret = v == null ? MReturn() : MReturn(parseExpr([v], false));
					mexprs.push(ret.at(expr.pos));
				case EBinop(op, e1, e2):
					switch(op)
					{
						default:
							mexprs.push(MExpr(expr).at(expr.pos));
						case OpLte:
							var bind = MBind(parseExpr([e1], false), parseExpr([e2], false), parseExpr(exprs));
							mexprs.push(bind.at(expr.pos));
					}
				case ECall(e, p):
					if (!outerScope)
						mexprs.push(MExpr(expr).at(expr.pos));
					else
						switch(Exprs.getIdent(e))
						{
							case Success(i):
								switch(Resolver.getFunc(i))
								{
									case None:
										mexprs.push(MExpr(expr).at(expr.pos));
									case Some(func):
										var v = [];
										for (param in p) v.push(MExpr(param).at(param.pos));
										mexprs.push(MCall(MExpr(func).at(e.pos), v, parseExpr(exprs)).at(e.pos));
								}
							case Failure(err):
								mexprs.push(MExpr(expr).at(expr.pos));
						}
			}
		}
		
		return mexprs.length == 1
			? mexprs[0]
			: MBlock(mexprs).at(Context.currentPos());
	}	
	

	#end
}